import {
  Component,
  ViewEncapsulation,
  ChangeDetectionStrategy,
  Input,
  ChangeDetectorRef,
  forwardRef,
  ContentChildren,
  QueryList,
  SimpleChanges,
  OnChanges,
  OnDestroy,
  AfterContentInit,
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Subject, merge } from 'rxjs';
import { takeUntil, startWith, auditTime, filter } from 'rxjs/operators';
import { InputBoolean, TypeUtils } from '../core/api';
import { RadioComponent } from './radio.component';

@Component({
  selector: 'lv-radio-group, [lv-radio-group]',
  exportAs: 'lvRadioGroup',
  template: ' <ng-content></ng-content> ',
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef((): typeof RadioGroupComponent => RadioGroupComponent),
      multi: true,
    },
  ],
  host: {
    '[class.lv-radio-group]': 'true',
    '[class.lv-radio-group-disabled]': 'lvDisabled',
  },
})
export class RadioGroupComponent implements ControlValueAccessor, OnChanges, AfterContentInit, OnDestroy {
  /**
   * Give the same name attribute to the input element in the group, so that
   * the browser can really think of radios under radio-group as a group
   * (for example, you can always change options in the same group by directional keys)
   */
  @Input() lvGroupName: string = '';
  @Input() @InputBoolean() lvDisabled;
  @ContentChildren(forwardRef((): typeof RadioComponent => RadioComponent), { descendants: true })
  radios: QueryList<RadioComponent>;

  private checksubscribe;
  private typeUtils = TypeUtils;
  private selected: string = '';

  private destroy$ = new Subject<void>();

  constructor(private cdr: ChangeDetectorRef) {}

  registerOnChange(fn: any): void {
    this._onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this._onTouch = fn;
  }

  isChecked(val): boolean {
    return this.selected === val ? true : false;
  }

  ngOnDestroy(): void {
    this.destroy$.next();
    this.destroy$.complete();
  }

  selectedChange(value): void {
    this.selected = value;
    this._onChange(value);
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes.lvDisabled) {
      this.updateRadios();
    }
  }

  writeValue(value: any): void {
    this.selected = value;
    this.updateRadios();
    this.cdr.markForCheck();
  }

  ngAfterContentInit(): void {
    // tslint:disable-next-line
    this.radios.changes.pipe(startWith(null), takeUntil(this.destroy$)).subscribe((): void => {
      setTimeout((): void => {
        this.updateRadios();
        this.subscribeChange();
      }, 0);
    });

    this.subscribeChange();
  }

  updateRadios(): void {
    if (this.radios) {
      this.radios.forEach((item): void => {
        if (this.selected === item.lvValue) {
          item.checked = true;
        } else {
          item.checked = false;
        }

        if (item.lvDisabled === undefined) {
          item.disabled = this.lvDisabled;
        }

        if (this.typeUtils.isRealString(this.lvGroupName)) {
          item.groupName = this.lvGroupName;
        }

        item.markForCheck();
      });
    }
  }

  subscribeChange(): void {
    if (this.checksubscribe) {
      this.checksubscribe.unsubscribe();
    }
    this.checksubscribe = merge(...this.radios.map((item): Subject<any> => item.select$))
      .pipe(
        auditTime(50),
        filter((res): boolean => !!res),
        takeUntil(this.destroy$),
      )
      .subscribe((item: any): void => {
        if (this.isChecked(item.lvValue)) {
          return;
        }
        this.selectedChange(item.lvValue);
        this.updateRadios();
      });
  }

  private _onTouch: any = (): void => {};

  private _onChange: any = (): void => {};
}
